﻿// UpdaterDialog.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Threading;

using Microsoft.ApplicationBlocks.Updater;

using Supremacy.Annotations;
using Supremacy.Client.Dialogs;
using Supremacy.Resources;
using Supremacy.Utility;

using DownloadProgressEventArgs = Microsoft.ApplicationBlocks.Updater.DownloadProgressEventArgs;

namespace Supremacy.Updater
{
    /// <summary>
    /// Interaction logic for UpdaterDialog.xaml
    /// </summary>
    public partial class UpdaterDialog
    {
        private readonly ApplicationUpdaterManager _updaterManager;
        private readonly Manifest[] _manifests;
        private int _manifestsDownloaded;
        private IEnumerable<UpdaterFile> _fileList;

        public UpdaterDialog([NotNull] ApplicationUpdaterManager updateManager, [NotNull] Manifest[] manifests)
        {
            if (updateManager == null)
                throw new ArgumentNullException("updateManager");
            if (manifests == null)
                throw new ArgumentNullException("manifests");

            InitializeComponent();
            
            _updaterManager = updateManager;
            _manifests = manifests;

            Initialize();
        }


        private void OnUnloadedEvent(object sender, RoutedEventArgs e)
        {
            _updaterManager.DownloadStarted -= OnDownloadStartedEvent;
            _updaterManager.DownloadCompleted -= OnDownloadCompletedEvent;
            _updaterManager.DownloadProgress -= OnDownloadProgressEvent;
            _updaterManager.DownloadError -= OnDownloadErrorEvent;

            _updaterManager.ActivationInitializationAborted -= OnActivationInitializationAbortedEvent;
            _updaterManager.ActivationCompleted -= OnActivationCompletedEvent;
        }

        private void Initialize()
        {
            _updaterManager.DownloadStarted += OnDownloadStartedEvent;
            _updaterManager.DownloadCompleted += OnDownloadCompletedEvent;
            _updaterManager.DownloadProgress += OnDownloadProgressEvent;
            _updaterManager.DownloadError += OnDownloadErrorEvent;

            _updaterManager.ActivationInitializationAborted += OnActivationInitializationAbortedEvent;
            _updaterManager.ActivationCompleted += OnActivationCompletedEvent;

            this.Loaded += OnLoadedEvent;
            this.Unloaded += OnUnloadedEvent;
        }

        private void OnDownloadStartedEvent(object sender, DownloadStartedEventArgs e)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate
                        {
                            if (DialogResult.HasValue)
                                _updaterManager.CancelDownload(e.Manifest);
                        });
        }

        private sealed class UpdaterFile : INotifyPropertyChanged
        {
            private string _file;
            private int _size;
            private double _progress;

            public string File
            {
                // ReSharper disable UnusedMember.Local
                get { return _file; }
                // ReSharper restore UnusedMember.Local
                set
                {
                    _file = value;
                    OnPropertyChanged("File");
                }
            }

            public int Size
            {
                get { return _size; }
                set
                {
                    _size = value;
                    OnPropertyChanged("Size");
                }
            }

            public double Progress
            {
                // ReSharper disable UnusedMember.Local
                get { return _progress; }
                // ReSharper restore UnusedMember.Local
                set
                {
                    _progress = value;
                    OnPropertyChanged("Progress");
                }
            }

            #region INotifyPropertyChanged Implementation
            [field: NonSerialized]
            public event PropertyChangedEventHandler PropertyChanged;

            private void OnPropertyChanged(string propertyName)
            {
                var handler = this.PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
        }

        private void OnLoadedEvent(object sender, RoutedEventArgs e)
        {
            _fileList = (from manifest in _manifests
                         from file in manifest.Files.OfType<FileManifest>()
                         select new UpdaterFile
                                {
                                    File = file.Source,
                                    Size = int.Parse(file.Attributes.Single(o => o.Name == "size").Value),
                                    Progress = 0.0
                                }).ToList();

            this.FileStatusList.ItemsSource = _fileList;

            // update the application as per manifest details.
            AsyncHelper.Invoke(
                (Action<Manifest[],TimeSpan>)_updaterManager.Download,
                _manifests,
                TimeSpan.MaxValue);
        }

        private void OnActivationCompletedEvent(object sender, ActivationCompleteEventArgs e)
        {
            if (e.Success)
                OnActivationInitializationAbortedEvent(sender, e);
        }

        private void OnActivationInitializationAbortedEvent(object sender, ManifestEventArgs e)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate
                        {
                            MessageDialog.Show(
                                ResourceManager.GetString("UPDATE_COMPLETE_HEADER"),
                                ResourceManager.GetString("UPDATE_COMPLETE_MESSAGE"),
                                MessageDialogButtons.Ok);
                            DialogResult = true;
                        });
        }

        private void OnDownloadErrorEvent(object sender, ManifestErrorEventArgs e)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate
                        {
                            MessageDialog.Show(
                                ResourceManager.GetString("UPDATE_ERROR_HEADER"),
                                String.Format(ResourceManager.GetString("UPDATE_ERROR_HEADER"), e.Exception.Message),
                                MessageDialogButtons.Ok);
                            DialogResult = false;
                        });
        }

        private void OnDownloadProgressEvent(object sender, DownloadProgressEventArgs e)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate
                        {
                            if (DialogResult.HasValue)
                                _updaterManager.CancelDownload(e.Manifest);
                            if (TotalProgress.Maximum != e.BytesTotal)
                                TotalProgress.Maximum = e.BytesTotal;
                            StatusText.Text = String.Format(
                                ResourceManager.GetString("UPDATE_DOWNLOAD_PROGRESS_MESSAGE"),
                                e.FilesTransferred + 1,
                                e.FilesTotal,
                                e.BytesTransferred / 1024.0 / 1024.0,
                                e.BytesTotal / 1024.0 / 1024.0);
                            TotalProgress.Value = e.BytesTransferred;
                        });
            UpdateFileProgress(e.BytesTransferred);
        }

        private void UpdateFileProgress(long transferred)
        {
            foreach (var file in _fileList)
            {
                file.Progress = (double)Math.Min(transferred, file.Size)  / file.Size;
                transferred -= file.Size;
                if (transferred <= 0)
                    break;
            }
        }

        private void OnDownloadCompletedEvent(object sender, ManifestEventArgs e)
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (Action)delegate
                        {
                            if (this.DialogResult.HasValue && !this.DialogResult.Value)
                                return;
                            TotalProgress.Value = TotalProgress.Maximum;
                            StatusText.Text = ResourceManager.GetString("UPDATE_STATUS_COMPLETE");
                            if (++_manifestsDownloaded == _manifests.Length)
                            {
                                _updaterManager.Activate(_manifests);
                            }
                        });
        }

        private void CancelButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (MessageDialog.Show(
                    ResourceManager.GetString("UPDATE_CANCEL_CONFIRM_HEADER"),
                    ResourceManager.GetString("UPDATE_CANCEL_CONFIRM_MESSAGE"),
                    MessageDialogButtons.YesNo) != MessageDialogResult.Yes)
            {
                return;
            }
            
            _updaterManager.CancelPendingUpdates();

            foreach (var manifest in _manifests)
                _updaterManager.CancelDownload(manifest);

            DialogResult = false;
        }
    }
}